

import scala.reflect.ClassTag

/**
  * description
  *
  * @author Llx
  * @version v1.0.0
  * @since 2018/10/8
  */
package object TraitTest {



  trait A{

    private var a:Int=1

    def a(x:Int):Int
  }
  trait B{
    private var b:Int=1

    def b(x:Int):Int
  }

  trait C{
    private var c:Int=1

    def c(x:Int):Int
  }

  class D extends A with B with C{

    override def a(x: Int): Int = ???

    override def b(x: Int): Int = ???

    override def c(x: Int): Int = {
      1 + x
    }
  }


  /**
    *
    * Scala类型系统编程实战：
    *
    * 1，Scala的类和方法、函数都可以是泛型，在Spark源码中可以到处看到类和方法的泛型，在实际实例化的时候指定具体
    *
    * 的类型，例如Spark最核心、最基础、最重要的抽象数据结构RDD里面关于RDD的类的定义是泛型的，RDD的几乎所有方法
    *
    * 的定义也都是泛型的，之所以这么做，是因为RDD会派生很多子类，通过子类适配了各种不同的数据源以及业务逻辑操作；
    *
    * 2， 关于对类型边界的限定，分为上边界和下边界：
    *
    * 上边界：表达了泛型的类型必须是某种类型或者某种类的子类，语法为 <:,这里的一个新的现象是对类型进行限定；
    *
    * 下边界：表达了泛型的类型必须是某种类型或者某种类的父类，语法为 >: ;
    *
    * 3, View Bounds,可以进行某种神秘的转换，把你的类型可以在没有知觉的情况下转换成为目标类型，其实你可以认为 View Bounds是
    *
    * 上边界和下边界的加强补充版本，例如在SparkContext这个Spark的核心类中有T <% Writable方式的代码，这个代码所表达的是
    *
    * T必须是Writable类型的，但是T有没有直接继承自Writable接口，此时就需要通过“implicit”的方式来实现这个功能；
    *
    * 4, T: ClassTag,例如Spark源码中的RDD class RDD[T: ClassTag] 这个其实也是一种类型转换系统，只是在编译的时候类型信息不够，
    *
    * 需要借助于JVM的runtime来通过运行时信息获得完整的类型信息，这在Spark中是非常重要的，因为Spark的程序的编程和运行是区分了
    *
    * Driver和Executor的，只有在运行的时候才知道完整的类型信息。
    *
    * 5, 逆变和协变；-T和+T  协变和逆变使用“+”,“-”差异标记。
    * 当我们定义一个协变类型List[+A]时，List[Child]可以是List[Parent]的子类型，
    * 当我们定义一个逆变类型List[-A]时，List[Child]可以是List[Parent]的父类型
    *   斜变
    * B----->A
    * List[B]----->List[A]
    *逆变
    * B----->A
    * List[A]----->List[B]
    *
    * 6, Conext Bounds,T: Ordering这种语法必须能够编程Ordering[T]这种方式；
    *
    */
  /**
    * 泛型  泛型函数  上下界限  视图界定
    *
    */


  /**
    * 泛型类 java
    * public class Animals<A,B> {
    *     private A name;
    *     private B age;
    * }
    *
    *  泛型类 scala
    *  class Animals[A,B](var name:A, var age:B) {
    *    println(s"Name is $name, Age is $age")
    *  }
    */

  /**
    * 泛型函数 Java
    * public <T> T convertJsonToObject(String jsonData, Class<T> clazz) {
    *   Gson gson = new Gson();
    *   T object =  gson.fromJson(jsonData, clazz);
    *   return object;
    * }
    *
    * protected static <T> List<T> asList(T[] pSrc) {
    *    if (pSrc == null || pSrc.length == 0) {
    *       return new ArrayList<T>();
    *   } else {
    *      return Arrays.asList(pSrc);
    *   }
    * }
    *
    * Scala实现
    * def asList[T](pSrc:Array[T]): List[T] = {
    *    if (pSrc == null || pSrc.isEmpty) {
    *        List[T]()
    *    } else {
    *       pSrc.toList
    *    }
    * }
    *
    */


  /**
    * ClassTag 、Manifest、ClassManifest、TypeTag代码实战及其在Spark中的应用源码解析
    * ClassTag ==> ClassManifest
    * TypeTag  ==> Manifest
    *
    *
    */

}
object TraitTest1{
  /**                           泛型数组的创建
    *
    *
    * [T: ClassTag]   ClassTag不能少
    *  Q:可以创建泛型数组吗? 理论上是不可以的，因为没有指定具体的，
    *  在Scala程序运行中，数组必须有具体的类型，没有否无法创建的相应的数组
    *
    *  引出Manifest的概念可以创建泛型数组
    * [T : Manifest]这样的写法被称之为Manifest上下文界定
    * 实质上这是需要一个Manifest[T]类型的隐式对象 这又是一个"隐式转换"的过程
    * 通过这个隐式的值来辅助构建泛型数组，来确定T的具体类型
    * 所以在创建泛型函数时 需要Manifest的类型来辅助构建泛型数组，借助Manifest类型对象来指定泛型数组具体的类型
    *
    *
    *
    * ClassManifest是Manifest的一个弱化版本，就是保存的类型信息不如Manifest多。
    * 不过scala在2.10里却用TypeTag替代了Manifest，用ClassTag替代了ClassManifest，
    * 原因是在路径依赖类型中，Manifest存在问题：
    * ClassTag ==> ClassManifest
    * TypeTag  ==> Manifest
    *
    */

  def arrayMake[T : Manifest](first: T, second: T) = {
    val r = new Array[T](2)
    r(0) = first
    r(1) = second
    r
  }

  /**
    * Manifest的原生写法  不推荐
    */
  def manif[T](x: List[T])(implicit m: Manifest[T]) = {
    if (m <:< manifest[String])   //<:< 表示 m是manifest[String]类型
      println("List Strings")
    else
      println("Some other type")
  }

  /**
    * [T : ClassTag] 这种写法说明 当这个函数在运行时时 对存在一个ClassTag[T]一个隐式值 这种方式是最常用的
    */
  def mkArray[T: ClassTag](elems: T*) = Array[T](elems: _*)

  def main(args: Array[String]): Unit = {
    val ints = mkArray(12,11)
    println(ints)

    println()
  }
}
